#include "InferenceEngine.h"

#include <QtSql>
#include <QVariant>
#include <QString>
#include <QDebug>

#include "../Common/GameInfo.h"
#include "Poker.h"
#include "../Common/Player.h"
#include "../Common/PokerTool.h"

InferenceEngine::InferenceEngine(GameInfo & _gameInfo)
{
 // Log file
 inferenceEngineLog = new QFile(INFERENCE_ENGINE_LOG);
 
 gameInfo = _gameInfo;
 
 sqlWhere = BuildSqlWhere();
}

QString InferenceEngine::BuildSqlWhere(int idCard)
{
 QString sqlWhere;

 int isSorted = 0;

 if(QString(gameInfo.getPlayer(1).getCard(0).getCardColor()) == QString(gameInfo.getPlayer(1).getCard(1).getCardColor()))
  isSorted = 1;  

 sqlWhere = "dpf.position = " + QString::number(gameInfo.getPlayer(1).getPosition()) + " ";
 
 if(idCard > 0)
  sqlWhere+= "AND _id_hand=" + QString::number(idCard) + " ";
 else
 {
  sqlWhere+= "AND (h.name = \"" + QString(gameInfo.getPlayer(1).getCard(0).getCardName()) + QString(gameInfo.getPlayer(1).getCard(1).getCardName()) + "\" ";
  sqlWhere+= "OR h.name = \"" + QString(gameInfo.getPlayer(1).getCard(1).getCardName()) + QString(gameInfo.getPlayer(1).getCard(0).getCardName()) + "\") ";
 }

 sqlWhere+= "AND dpf.is_sorted=" + QString::number(isSorted) + " ";

 // If the game is raised
 // if(gameInfo.getAmountRaised() > 0)
 //  sqlWhere+= "AND dpf.is_raised=1 ";
 
 // Limit amount to call = Amount raised / BB
 // QString limitAmountToCall = QString::number(gameInfo.getAmountRaised()/gameInfo.getBigBlind());
 // sqlWhere += "AND dpf.limit_amount_to_call >= " + limitAmountToCall + " ";
 
 return sqlWhere;
}

GameInfo InferenceEngine::LaunchInferenceEngine()
{
	// Create the connection
	db = QSqlDatabase::addDatabase(DRIVER);
	db.setDatabaseName(QString(DATABASE) + "_" + gameInfo.getPlayer(1).getStrategy());
	db.setUserName(USER);
	db.setPassword(PASSWORD);
	db.setHostName(HOST);

	if(db.open())
	{
		Match();
  		ConflictResolution();
 		Act();

		// Close the connection
		db.close();
	}
	else
	{
  		std::cout << "ERROR : db.open()" << std::endl;
	}
	
	return this->gameInfo;
}

/**
 * Search the overall inferences rule that can be applied
 * to a certain moment
 */
void InferenceEngine::Match()
{ 
 sql = "SELECT * FROM decision_pre_flop dpf, hand h ";
 sql+= "WHERE " + sqlWhere;

 // "Jointure"
 sql+= "AND dpf._id_hand=h._id_hand";

 qDebug() << "Sql: " << sql;
 
 queryModel = new QSqlQueryModel();
 queryModel->setQuery(sql);
 
 for(int i=0; i<queryModel->rowCount(); ++i)
 {
  int id = queryModel->record(i).value("_id_decision_pre_flop").toInt();
  QString name = queryModel->record(i).value("hand").toString();
  qDebug() << id << name;
  
  int action = queryModel->record(i).value("action").toInt();
  qDebug() << "Action: " << QString::number(action);
  
  if(action == 2)
  {
   int amountRaised = queryModel->record(i).value("amount_raised").toInt();
   qDebug() << "Relance de " << amountRaised;
  }
 }
}

/**
 * The engine is going to choose among all the rules
 * that he can use, the one that it is going to execute
 */
void InferenceEngine::ConflictResolution()
{
 if(queryModel->rowCount() > 1 && inferenceEngineLog->open(QIODevice::Append | QIODevice::Text) && gameInfo.getPlayer(1).getStrategy() == "static")
 {
  QString errorLog = "";
  errorLog += "Date: " + QDateTime::currentDateTime().toString("MM-dd-yyyy hh:mm:ss") + "\n";
  errorLog += "Error: " + QString::number(queryModel->rowCount()) + " rules found...\n";
  errorLog += "SQL query: " + sql + "\n";
  errorLog += "\n";

  inferenceEngineLog->write(errorLog.toAscii());

  inferenceEngineLog->close();
 }
}

/**
 * After choosing the rule, the inference engine execute it (update the database)
 */
void InferenceEngine::Act()
{
	qDebug() << "Function Act";

	if(gameInfo.getPlayer(1).getStrategy() == "static")
	{
		qDebug() << "Bot static";
  
		int action = queryModel->record(0).value("action").toInt();
		int amountRaised = queryModel->record(0).value("amount_raised").toInt();

		gameInfo.getPlayer(1).setAction(action);

		// Set l'indice du montant de relance
		gameInfo.getPlayer(1).setAmountRaised(amountRaised);

		// Set le montant de relance
		gameInfo.getPlayer(1).setMise(amountRaised*BIGBLIND);

		std::cout << "gameInfo.getCurrentPlayer() : "  << gameInfo.getCurrentPlayer() << std::endl;
		std::cout << "action joueur : " << action << std::endl;
		std::cout << "action amountRaised : " << amountRaised << std::endl;
	}
	else if(gameInfo.getPlayer(1).getStrategy() == "learning")
	{
	 qDebug() << "Bot learning";

	  // Insert call rule
	  if(queryModel->rowCount() == 0)
	  {
       gameInfo.getPlayer(1).setAction(1);
       gameInfo.getPlayer(1).setMise(gameInfo.getPlayer(1).getAmountToCall());

	   if(gameInfo.getGameState() == 5)
	   {
		qDebug() << "Learn call action";
	    InsertNewRule(1);
	   }
	  }
	  // Insert raise rule
	  else if(queryModel->rowCount() == 1)
	  {
	   gameInfo.getPlayer(1).setAction(2);
	   gameInfo.getPlayer(1).setMise(BIGBLIND + gameInfo.getPlayer(1).getAmountToCall());

	   if(gameInfo.getGameState() == 5)
	   {
		qDebug() << "Learn raise action";
		InsertNewRule(2);
	   }
	  }
	  // All possibilities are in the database (call and raise)
	  else if(queryModel->rowCount() == 2)
	  {
//	   int hand_played_call  = queryModel->record(0).value("hand_played").toInt();
//	   int hand_played_raise = queryModel->record(1).value("hand_played").toInt();
//
//	   if(hand_played_call == 10 && hand_played_raise == 10)
//	   {
		qDebug() << "Learning is finished, search the best action...";

		int fitness_call  = queryModel->record(0).value("fitness").toInt();
		int fitness_raise = queryModel->record(0).value("fitness").toInt();

		if(fitness_call >= fitness_raise)
		{
	     gameInfo.getPlayer(1).setAction(1);
	     gameInfo.getPlayer(1).setMise(gameInfo.getPlayer(1).getAmountToCall());
		}
		else if(fitness_call < fitness_raise)
		{
		 gameInfo.getPlayer(1).setAction(2);
		 gameInfo.getPlayer(1).setMise(BIGBLIND + gameInfo.getPlayer(1).getAmountToCall());
		}
//		else
//		{
		 // Conflit
		 // Fichier de log
		 // Mettre en attribut de la classe le nombre de fois que l'on doit attendre pour
		 // commencer à jouer
//		}
//	   }
//       else
//	   {
		if(gameInfo.getGameState() == 5)
	    {
	     int idCard = queryModel->record(0).value("_id_hand").toInt();
	     sqlWhere = BuildSqlWhere(idCard);

	     QString sql = "";
	     sql += "UPDATE decision_pre_flop dpf ";
	     sql += "SET dpf.hand_played = dpf.hand_played+1, ";
	     sql += "dpf.fitness = ((dpf.fitness * (dpf.hand_played-1)) + " + QString::number(gameInfo.getPlayer(1).getFitness()) + ") / dpf.hand_played ";
	     sql += "WHERE " + sqlWhere + " ";

//	    if(queryModel->record(0).value("hand_played").toInt() == queryModel->record(1).value("hand_played"))
//		 sql += "AND action=1";
//	    else

	     sql += "AND action=" + QString::number(gameInfo.getPlayer(1).getAction(gameInfo.getPlayer(1).getAction().toStdString()));

	     qDebug() << sql;

	     QSqlQuery sqlQuery(sql);
	    }
	  }
   }
}

/**
 * Add a rule into the database
 * 
 * TODO : - add fitness field
 *        - add hand_played field
 */
void InferenceEngine::InsertNewRule(int action)
{
 QString sql = "";
 sql += "INSERT INTO decision_pre_flop ";
 sql += "(_id_hand, is_sorted, position, is_raised, hand_played, fitness, action) ";
 sql += "SELECT _id_hand, ";
 sql += QString::number(Poker::isSorted(gameInfo.getPlayer(1).getCard(0).getCardColor(), gameInfo.getPlayer(1).getCard(1).getCardColor())) + ", ";
 sql += QString::number(gameInfo.getPlayer(1).getPosition()) + ", ";
 sql += QString::number(Poker::isRaised(gameInfo.getPlayer(1).getAmountRaised())) + ", ";
// sql += QString::number(gameInfo.getAmountRaised()/gameInfo.getBigBlind()) + ", ";
//
// if(action == 1)
//  sql += "0, ";
// else if(action == 2)
//  sql += "1, ";
// else
// {
//  // Log file
// }

 sql += "1, ";
 sql += QString::number(gameInfo.getPlayer(1).getFitness()) + ", ";

 sql += QString::number(action) + " ";
 sql += "FROM hand ";
 sql += "WHERE (name = \"" + QString(gameInfo.getPlayer(1).getCard(0).getCardName()) + QString(gameInfo.getPlayer(1).getCard(1).getCardName()) + "\" ";
 sql += "OR name = \"" + QString(gameInfo.getPlayer(1).getCard(1).getCardName()) + QString(gameInfo.getPlayer(1).getCard(0).getCardName()) + "\")";

 qDebug() << sql;

 QSqlQuery sqlQuery(sql);
}

